/**
 * Copyright [2010] Delta Air Lines, Inc. All Rights Reserved Access,
 * Modification, or Use Prohibited Without Express Permission of Delta Air
 * Lines.
 */
package com.delta.rafg.core.base;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import com.delta.rafg.core.config.ConfigSettings;
import com.delta.rafg.core.exception.RAFGException;
import com.delta.rafg.core.log.LogData;
import com.delta.rafg.core.log.RAFGLogger;
import com.delta.rafg.coredata.ARequest;
import com.delta.rafg.coredata.AResponse;
import com.delta.rafg.coredata.XMLReqString;
import com.delta.rafg.coredata.XMLResString;


/**
 * ABackendInterface.java: Generic class.
 * @author Delta
 * @version 1.0
 * @see : IAWorkerBean.
 */
public abstract class ABackendInterface extends AWorkerBean {

    /**
     * Holds the value of serialVersionUID property.
     */
    private static final long serialVersionUID = 1L;

	private static String ONINTFENTRY 		= "ONINTFENTRY";
	private static String ONINTFEXIT 		= "ONINTFEXIT";
	private static String BEFOREBACKENDCALL = "BEFOREBACKENDCALL";
	private static String AFTERBACKENDCALL 	= "AFTERBACKENDCALL";
	
	/**
	 * To be called as the first line of a interface method.
	 *
	 * @param pRequest
	 * @return
	 */
	protected LogData ENTERINTF(ARequest pRafgRequest){
		return RAFGLogger.warn(ONINTFENTRY, pRafgRequest);
	}

	/**
	 * To be called as the last line before the return of a interface method.
	 *
	 * @param pRequest
	 * @param pResponse
	 * @param pOnEntryLogData
	 * @return
	 */
	protected LogData EXITINTF(ARequest pRafgRequest, AResponse pRafgResponse, LogData pOnEntryLogData){
		LogData anExitLogData = RAFGLogger.warn(ONINTFEXIT, (Serializable)pRafgRequest, (Serializable)pRafgResponse);
		anExitLogData.captureTimeTaken(pOnEntryLogData);
		RAFGLogger.warn("RAFG Interface Call " + anExitLogData.getTimeTaken());
		return anExitLogData;
	}


	/**
	 * To be called right BEFORE making a call to a backend interface such as SOA.
	 *
	 * @param pRequest
	 * @return
	 */
	protected LogData BEFOREBACKENDCALL(ARequest pRafgRequest, Object pBackendRequest){
		return BEFOREBACKENDCALL(pRafgRequest, pBackendRequest, false);
	}
	
	/**
	 * To be called right BEFORE making a call to a backend interface such as SOA.
	 *
	 * @param pRequest
	 * @return
	 */
	protected LogData BEFOREBACKENDCALL_INSIGNIFICANT(ARequest pRafgRequest, Object pBackendRequest){
		return BEFOREBACKENDCALL(pRafgRequest, pBackendRequest, true);
	}
	
	// do not need to check for this instance so using instance variable to hold this.
	Boolean mustLogForUser = null;
	
	private LogData BEFOREBACKENDCALL(ARequest pRafgRequest, Object pBackendRequest, boolean pInsignificant){
		
		// Check if the log level is STATS_ONLY so we have a way to turn this off
		boolean shouldLogPerLogLevel = RAFGLogger.shouldLog(LogData.STATS_ONLY);
		
		// Check if logging is enabled for specific user, workstation or city 
		//	- if not checked already and if logging is not enable in Logger
		if( !shouldLogPerLogLevel && mustLogForUser == null)
			mustLogForUser = new Boolean(shouldLogForUserInfo(pRafgRequest));
		
		// Continue logging if shouldLogPerLogLevel is true  
		//	 - or if must log for this user, workstation or city
		if( ( mustLogForUser != null && mustLogForUser.booleanValue() ) || shouldLogPerLogLevel ){
			
			setTransactionId(pRafgRequest, pBackendRequest);
			String theReq = getSoapRequestString(pBackendRequest);
			String rafgData = getUsefulFromRafgRequest(pRafgRequest);
			prevSoapReq = theReq;

			if( ( mustLogForUser != null && mustLogForUser.booleanValue() ) ){
				//Logging must happen for this user, workstation or city
				return RAFGLogger.error(BEFOREBACKENDCALL, pRafgRequest, new XMLReqString(prefixUsefulData(theReq) + rafgData));
			}
			else if( !pInsignificant ){
				return RAFGLogger.stats(BEFOREBACKENDCALL, pRafgRequest, new XMLReqString(prefixUsefulData(theReq) + rafgData));
			}
			else{
				// the caller has request this to be Insignificant so log it as a warning
				return RAFGLogger.warn(BEFOREBACKENDCALL, pRafgRequest, new XMLReqString(prefixUsefulData(theReq) + rafgData));
			}
		}
		else{
			return RAFGLogger.unLoggedData(LogData.STATS_ONLY);
		}
	}
	
	/**
	 * This methos checks if the user id or workstation or city is listed in  ConfigSettings.AXS_CITY_WKS_USERS_TOLOG
	 * 
	 * @param pRafgRequest
	 * @return
	 */
	private boolean shouldLogForUserInfo(ARequest pRafgRequest){
		try{
			List<String> lstLoggableUsers = getLoggableCitiesWorkstationsUsers();
			if( lstLoggableUsers != null && pRafgRequest != null ){
				return (lstLoggableUsers.contains(pRafgRequest.getAgentId()) 
						|| lstLoggableUsers.contains(pRafgRequest.getAgentCity()) 
						|| lstLoggableUsers.contains(pRafgRequest.getAgentCity()) ); 
			}
			
		}
		catch(Throwable t){}
		
		return false;
	}
	
	
	/**
	 * Gets the users for whom all backend requests are to be logged.
	 * This comes from ConfigSettings or if not present in ConfigSettings from a JVM env variable.
	 * 
	 * @return
	 */
	private List<String> getLoggableCitiesWorkstationsUsers() {
		String loggageUsrsStr = "";
		
		String environment = System.getProperty("com.delta.rafg.AXS_CITY_WKS_USERS_TOLOG");
		loggageUsrsStr = environment != null && !environment.isEmpty() ? environment : loggageUsrsStr;
		
		try {
			String loggageUsrsStrDB = ConfigSettings.getSingleton().getProperty("AXS_CITY_WKS_USERS_TOLOG");
			loggageUsrsStr = loggageUsrsStrDB != null && !loggageUsrsStrDB.isEmpty() ? loggageUsrsStrDB : loggageUsrsStr;
		} catch (RAFGException e) {}
		
		if("".equals(loggageUsrsStr))
			return null;
		else
			return Arrays.asList(loggageUsrsStr.split(";"));
	}	

	String prevSoapReq = "";
	
	/**
	 * To be called right AFTER making a call to a backend interface such as SOA.
	 *
	 * @param pRequest
	 * @param pResponse
	 * @param pOnEntryLogData
	 * @return
	 */
	protected void AFTERBACKENDCALL(ARequest pRafgRequest, Object pBackendRequest, Object pBackendResponse, LogData pBeforeLogData){
		AFTERBACKENDCALL(pRafgRequest, pBackendRequest, pBackendResponse, pBeforeLogData, false);
	}
	
	/**
	 * To be called right AFTER making a call to a backend interface such as SOA.
	 * 	This method is to be used for backend requests that are not important to be logged.
	 *
	 * @param pRequest
	 * @param pResponse
	 * @param pOnEntryLogData
	 * @return
	 */
	protected void AFTERBACKENDCALL_INSIGNIFICANT(ARequest pRafgRequest, Object pBackendRequest, Object pBackendResponse, LogData pBeforeLogData){
		AFTERBACKENDCALL(pRafgRequest, pBackendRequest, pBackendResponse, pBeforeLogData, true);
	}
	
	private void AFTERBACKENDCALL(ARequest pRafgRequest, Object pBackendRequest, Object pBackendResponse, LogData pBeforeLogData, boolean pInsignificant){

		// Check if the log level is STATS_ONLY so we have a way to turn this off
		boolean shouldLogPerLogLevel = RAFGLogger.shouldLog(LogData.STATS_ONLY);
		
		// Check if logging is enabled for specific user, workstation or city 
		//	- if not checked already and if logging is not enable in Logger
		if( !shouldLogPerLogLevel && mustLogForUser == null)
			mustLogForUser = new Boolean(shouldLogForUserInfo(pRafgRequest));
		
		// Continue logging if shouldLogPerLogLevel is true  
		//	 - or if must log for this user, workstation or city
		if( ( mustLogForUser != null && mustLogForUser.booleanValue() ) || shouldLogPerLogLevel ){
			
			String theReq = getSoapRequestString(pBackendRequest);
			String rafgData = getUsefulFromRafgRequest(pRafgRequest);
			
			//logged already to ignore
			if ((prevSoapReq != null && prevSoapReq.equals(theReq))){
				theReq = "";
			}
			else{
				theReq = prefixUsefulData(theReq) + rafgData;
			}
			
			String theRes = getSoapString(pBackendResponse);
			
			LogData anExitLogData = null;
			
			if( ( mustLogForUser != null && mustLogForUser.booleanValue() ) ){
				// Logging must happen for this user, workstation or city
				anExitLogData = RAFGLogger.error(AFTERBACKENDCALL, pRafgRequest, new XMLReqString(theReq), new XMLResString(prefixUsefulData(theRes) + rafgData));
			}
			else if( !pInsignificant )
				anExitLogData = RAFGLogger.stats(AFTERBACKENDCALL, pRafgRequest, new XMLReqString(theReq), new XMLResString(prefixUsefulData(theRes) + rafgData));
			else
				anExitLogData = RAFGLogger.warn(AFTERBACKENDCALL, pRafgRequest, new XMLReqString(theReq), new XMLResString(prefixUsefulData(theRes) + rafgData));
			
			if( anExitLogData != null ){
				anExitLogData.captureTimeTaken(pBeforeLogData);
				RAFGLogger.warn("Backend Call " + anExitLogData.getTimeTaken());
			}
		}
	}	

	private String prefixUsefulData(String pString){
		String retVal = pString;
		try{
			if ( !retVal.startsWith("\n") ){
				retVal = "\n" + retVal;
			}
			if( pString != null && !pString.isEmpty() ){
				
				retVal = "####### " 
						
						+ "[" + getAttributeFromXml(retVal, " SessionId") + "]"
						+ " "
						
						+ "[" + getAttributeFromXml(retVal, " LogonId") 
						+ "/" + getAttributeFromXml(retVal, " City") 
						+ "/" + getAttributeFromXml(retVal, " DutyCode") +"]"
						+ " "
						
						+ "[" + getAttributeFromXml(retVal, " RecordLocator") +"]"
						+ " "
						
						+ "[" + getAttributeFromXml(retVal, " TransactionId") +"]"
						+ " "

						+ " #######" + retVal;
			}
		}
		catch(Throwable t){}
		
		return retVal;
	}
	
	private String getAttributeFromXml(String pString, String pAttribute){
		String retVal = "";
		try{
			if( pString != null && !pString.isEmpty() && pAttribute != null && !pAttribute.isEmpty() ){
				if(!pAttribute.endsWith("=\"")){
					pAttribute = pAttribute + "=\""; 
				}
				int startIndex = pString.indexOf(pAttribute);
				int endIndex = pString.indexOf("\"",startIndex + pAttribute.length());
				if( startIndex > -1 && endIndex > startIndex){
					retVal = pString.substring(startIndex + pAttribute.length(), endIndex );
				}
			}
		}
		catch(Throwable t){}
		return retVal;
	}
	
	private String getUsefulFromRafgRequest(ARequest pRafgRequest){
		String retVal = "";
		try{
			if( pRafgRequest != null ){
				retVal += "{" + checkNull(pRafgRequest.getAgentId());
				retVal += "@" + checkNull(pRafgRequest.getAgentCity());
				retVal += "@" + checkNull(pRafgRequest.getAgentDeviceId());
				retVal += ":" + checkNull(pRafgRequest.getClientSessionId());
				retVal += "}\n";
			}
		}
		catch(Throwable t){}
		return retVal;
	}
	
	private String checkNull(String pString){
		return (pString == null) ? "" : pString;
	}
	
	private void setTransactionId(ARequest pRafgRequest, Object pBackendRequest){
		if( pBackendRequest != null){
			try{
				String agentId = null; 
				
				if( pRafgRequest != null )
					agentId = pRafgRequest.getAgentId();
				
				Object reqInfo = pBackendRequest.getClass().getMethod("getRequestInfo").invoke(pBackendRequest, (Object[])null);
				if( reqInfo != null){
					if( agentId == null || agentId.isEmpty() ){
						try{
							Object objAgentId = reqInfo.getClass().getMethod("getLogonId").invoke(reqInfo, (Object[])null);
							agentId = objAgentId == null ? agentId : objAgentId.toString(); 
						}
						catch(Throwable t){}
					}
					agentId = ( agentId == null || agentId.isEmpty() )? "000000" : agentId;
					
					String execWSine = "NO_WSINE";
					String clientDvc = "NO_CLNT_INFO";
					String clientSID = "NO_JSESS_ID";
					Object objSessionId = null;
					try{
						objSessionId = reqInfo.getClass().getMethod("getSessionId").invoke(reqInfo, (Object[])null);
					}
					catch(Throwable t){}
					
					if( objSessionId != null && objSessionId.toString().indexOf("executionWsine=")>0){
						execWSine = objSessionId.toString().substring(objSessionId.toString().indexOf("executionWsine=") + "executionWsine=".length());
					}
					
					if( pRafgRequest != null && pRafgRequest.getAgentDeviceId() != null 
							&& !pRafgRequest.getAgentDeviceId().isEmpty()){
						clientDvc = pRafgRequest.getAgentDeviceId();
						clientDvc = clientDvc.length() > 15 ? clientDvc.substring(0,15) : clientDvc;
					}
					
					if( pRafgRequest != null && pRafgRequest.getClientSessionId() != null 
							&& !pRafgRequest.getClientSessionId().isEmpty()){
						clientSID = pRafgRequest.getClientSessionId();
					}
					
					String strTranId = agentId + ":" + clientDvc + ":" + clientSID + ":" + execWSine;  
					
					reqInfo.getClass().getMethod("setTransactionId", java.lang.String.class).invoke(reqInfo, strTranId);
				}
			}
			catch(Throwable t){}
		}
	}

}
